//
//  IpaCompare.swift
//  JKTool
//
//  Created by liuyan on 2023/6/5.
//

import Foundation
extension JKTool {
    struct IpaCompare: ParsableCommand {
        static var configuration = CommandConfiguration(
            commandName: "ipaCompare",
            _superCommandName: "JKTool",
            abstract: "ipaCompare命令用于ipa分析结果比较",
            version: "1.0.0"
        )
        
        @Option(name: .shortAndLong, help: "基准ipa分析路径")
        var baseIpaParseInfoPath: String
        
        @Option(name: .shortAndLong, help: "当前ipa分析路径")
        var currentIpaParseInfoPath: String
        
        @Option(name: .shortAndLong, help: "对比结果保存路径")
        var resultPath: String?
        
        var compareResult = ""
        
        mutating func run() throws {
            po(tip: "======ipa包大小比较开始======")
            let date = Date.init().timeIntervalSince1970
            
            let fileManager = FileManager.default
            
            guard fileManager.fileExists(atPath: baseIpaParseInfoPath) else {
                po(tip: "请选择正确的基准ipa分析文件路径", type: .error)
                return
            }
            
            guard fileManager.fileExists(atPath: currentIpaParseInfoPath) else {
                po(tip: "请选择正确的当前ipa分析文件路径", type: .error)
                return
            }
            
            // 清理结果保存路径
            var resultSavePath = resultPath ?? "ipaCompareInfo.txt"
            if fileManager.isDirectory(path: resultSavePath) {
                resultSavePath = "\(resultSavePath)/ipaCompareInfo.txt"
            }
            
            if fileManager.fileExists(atPath: resultSavePath) {
                do {
                    try fileManager.removeItem(atPath: resultSavePath)
                } catch {
                    po(tip: "ipaCompareInfo文件清理失败\r\n\(error)", type: .error)
                    return
                }
            }
            
            guard let baseIpaParseInfo = try? String(contentsOfFile: baseIpaParseInfoPath, encoding: .utf8), let currentIpaParseInfo = try? String(contentsOfFile: currentIpaParseInfoPath, encoding: .utf8) else {
                po(tip: "ipa分析文件读取失败", type: .error)
                return
            }
            
            let linkMapStartSymbol = "# LinkMap Parse Start"
            let linkMapEndSymbol = "# LinkMap Parse End"
            var shouldCommpareLinkMap = false
            if let _ = baseIpaParseInfo.range(of: linkMapStartSymbol), let _ = currentIpaParseInfo.range(of: linkMapStartSymbol) {
                shouldCommpareLinkMap = true
            }

            var baseIpaSizeMap = [String: String]()
            var baseLinkMapStart = false
            var lines = baseIpaParseInfo.components(separatedBy: "\r\n")
            for line in lines {
                let array = line.components(separatedBy: "\t")
                if array.count == 2 {
                    // 如果不需要比较 linkMap，则跳过 linkMap 的内容
                    if !shouldCommpareLinkMap && baseLinkMapStart {
                        continue
                    }
                    
                    baseIpaSizeMap[array[1]] = array[0]
                } else if line.hasPrefix(linkMapStartSymbol) {
                    baseLinkMapStart = true
                } else if line.hasPrefix(linkMapEndSymbol) {
                    baseLinkMapStart = false
                }
            }
            
            // 处理修改和新增的文件对比
            lines = currentIpaParseInfo.components(separatedBy: "\r\n")
            var currentLinkMapStart = false
            for line in lines {
                let array = line.components(separatedBy: "\t")
                if array.count == 2 {
                    let path = array[1]
                    let currentSize = array[0]
                    
                    // 如果不需要比较 linkMap，则跳过 linkMap 的内容
                    if !shouldCommpareLinkMap && currentLinkMapStart {
                        continue
                    }
                    
                    let baseSize = baseIpaSizeMap[path] ?? "0B"
                    recordFile(atPath: path, baseSizeString: baseSize, currentSizeString: currentSize)
                    
                    baseIpaSizeMap.removeValue(forKey: path)
                } else if line.hasPrefix(linkMapStartSymbol) {
                    currentLinkMapStart = true
                } else if line.hasPrefix(linkMapEndSymbol) {
                    currentLinkMapStart = false
                }
            }
            
            // 处理删除的文件
            let deletedFiles = Array(baseIpaSizeMap.keys).sorted { path1, path2 in
                return path1.components(separatedBy: "/").count < path2.components(separatedBy: "/").count
            }
            
            for file in deletedFiles {
                if let recordFileString = recordFileString(atPath: file, baseSizeString: baseIpaSizeMap[file] ?? "0B", currentSizeString: "0B") {
                    var array = file.components(separatedBy: "/")
                    if array.count > 1 {
                        array.removeLast()
                        let directoryPath = array.joined(separator: "/")
                        if let range = self.compareResult.range(of: "\(directoryPath)\r\n") {
                            compareResult.insert(contentsOf: recordFileString, at: range.upperBound)
                        } else {
                            compareResult.append(recordFileString)
                        }
                    } else {
                        compareResult.append(recordFileString)
                    }
                }
            }
            
            do {
                try compareResult.write(toFile: resultSavePath, atomically: true, encoding: .utf8)
            } catch {
                po(tip: "ipa比较结果保存失败\r\n\(error)", type: .error)
                return
            }
            
            po(tip: "======ipa包大小比较完成[\(GlobalConstants.duration(to: date) + " s")]======")
        }
        
        func recordFileString(atPath path: String, baseSizeString: String, currentSizeString: String) -> String? {
            let baseSize = sizeFormString(baseSizeString)
            let currentSize = sizeFormString(currentSizeString)
            let changeSize = currentSize - baseSize
            if changeSize != 0 {
                let changeSizeString = stringFormSize(changeSize)
                return String(format: "%@\t%@\t%@\t%@\r\n", baseSizeString, currentSizeString, changeSizeString, path)
            }
            return nil
        }
        
        mutating func recordFile(atPath path: String, baseSizeString: String, currentSizeString: String) {
            if let string = recordFileString(atPath: path, baseSizeString: baseSizeString, currentSizeString: currentSizeString) {
                compareResult.append(string)
            }
        }
        
        func sizeFormString(_ sizeString: String) -> Int {
            var size: Int = 0
            if sizeString.contains("KB") {
                size = Int((Double(sizeString.replacingOccurrences(of: "KB", with: "")) ?? 0) * 1024)
            } else if sizeString.contains("MB") {
                size = Int((Double(sizeString.replacingOccurrences(of: "MB", with: "")) ?? 0) * 1024 * 1024)
            } else if sizeString.contains("B") {
                size = Int(Double(sizeString.replacingOccurrences(of: "B", with: "")) ?? 0)
            } else {
                size = Int(sizeString) ?? 0
            }
            return size
        }
        
        func stringFormSize(_ size: Int) -> String {
            var sizeString = ""
            if abs(Double(size) / 1024.0 / 1024.0) > 1 {
                sizeString = String(format: "%.2fMB", Double(size) / 1024.0 / 1024.0)
            } else {
                sizeString = String(format: "%.2fKB", Double(size) / 1024.0)
            }
            return sizeString
        }
    }
}
